package internal

import (
	"container/heap"
	"fmt"
	"sync"
	"time"

	log "github.com/sirupsen/logrus"

	"GameServer/Common/data"
	"GameServer/Common/utils"
	"GameServer/Game/dbmodule"
	"GameServer/Game/m"
)

// 放火，规则
// 新手最大关是第一关，可以匹配别人，别人不能匹配他。
// 玩家当前层打开的格子数少于2，不进入匹配
// 玩家被匹配，标记为使用。

// 匹配
// @todo 有性能问题
type MatchPool struct {
	rwFire           *sync.RWMutex
	mFireMatch       map[string]string    // 放火匹配池
	mFireUse         map[string]string    // map[uida]uidb，玩家uida被玩家uidb放火
	addFireProtec    chan *ProtectionUser // 放火添加保护用户
	removeFireProtec chan string          // 放火移除保护用户
	protecFire       PriorityQueue        // 放火保护列表
	mFireProtect     map[string]bool      // 放火保护标记
}

type ProtectionUser struct {
	Uid     string // 受保护的玩家
	EndTime int64  // 受保护的结束时间
	index   int
}

type PriorityQueue []*ProtectionUser

func (pq PriorityQueue) Len() int { return len(pq) }
func (pq PriorityQueue) Less(i, j int) bool {
	return pq[i].EndTime < pq[j].EndTime
}
func (pq PriorityQueue) Swap(i, j int) {
	pq[i], pq[j] = pq[j], pq[i]
	pq[i].index = i
	pq[j].index = j
}
func (pq *PriorityQueue) Push(x interface{}) {
	n := len(*pq)
	item := x.(*ProtectionUser)
	item.index = n
	*pq = append(*pq, item)
}
func (pq *PriorityQueue) Pop() interface{} {
	old := *pq
	n := len(old)
	item := old[n-1]
	item.index = -1 // for safety
	*pq = old[0 : n-1]
	return item
}
func (pq *PriorityQueue) update(item *ProtectionUser, Uid string, EndTime int64) {
	item.Uid = Uid
	item.EndTime = EndTime
	heap.Fix(pq, item.index)
}

func (this *MatchPool) getLayerTByTid(tid uint64) (r data.Layer, ok bool) {
	val, ok := data.Get("Layers", tid)
	if !ok {
		return
	}
	r = val.(data.Layer)
	return
}

func (this *MatchPool) CheckLegal(lockBlockTid int64, us *m.User) (ok bool) {
	// 1、打开的格子数 <=2
	// 2、新手保护
	// 3、关卡信息相差大于9或小于9
	// 不进入匹配
	if us.Pos.LayerOpenGridNum <= 5 ||
		us.MaxPos.LockBlockTid <= data.SlotFreshmanProtectEnum.FreshManCardId ||
		us.MaxPos.LockBlockTid < lockBlockTid-data.SlotLockBlockLimit ||
		us.MaxPos.LockBlockTid > lockBlockTid+data.SlotLockBlockLimit {
		return false
	}
	layerTemp := us.GetLayer()
	layerT, ok := this.getLayerTByTid(uint64(layerTemp.Tid))
	if ok && layerT.Layer_type == 1 {
		// 玩家在boss楼层，不进入匹配
		return false
	}
	return true
}

// 给玩家匹配放火玩家
func (this *MatchPool) MatchFireUser(us *m.User) (ruid string, ok bool) {
	this.rwFire.Lock()
	defer this.rwFire.Unlock()
	var eus *m.User = nil
	uid := us.Uid.Hex()
	cnt := 0
	for key := range this.mFireMatch {
		if key == uid { // 过滤掉自己
			continue
		}
		if _, tok := this.mFireUse[key]; tok { // 玩家被使用
			continue
		}
		if _, tok := this.mFireProtect[key]; tok { //玩家被保护
			continue
		}
		eus = m.GetUser(key)
		if this.CheckLegal(us.MaxPos.LockBlockTid, eus) {
			break
		}
		eus = nil
		cnt++
		if cnt >= 100 { // 为了效率，检查了100个玩家，没有合法的，就不会再检测玩家了。
			break
		}
	}
	// 被匹配的玩家，标记为使用
	if eus == nil {
		return "", false
	}
	euid := eus.Uid.Hex()
	this.mFireUse[euid] = euid
	return euid, true
}

// 给玩家匹配老虎机玩家 匹配 lst中的玩家
func (this *MatchPool) MatchFireUserLst(us *m.User, lst []string) (ruid string, ok bool) {
	this.rwFire.Lock()
	defer this.rwFire.Unlock()
	var eus *m.User = nil
	uid := us.Uid.Hex()
	cnt := 0
	for _, item := range lst {
		if item == uid { // 过滤掉自己
			continue
		}
		if _, tok := this.mFireUse[item]; tok { // 玩家被使用
			continue
		}
		if _, tok := this.mFireProtect[item]; tok { //玩家被保护
			continue
		}
		eus = m.GetUser(item)
		if this.CheckLegal(us.MaxPos.LockBlockTid, eus) {
			break
		}
		eus = nil
		cnt++
		if cnt >= 100 { // 为了效率，检查了100个玩家，没有合法的，就不会再检测玩家了。
			break
		}
	}
	// 被匹配的玩家，标记为使用
	if eus == nil {
		return "", false
	}
	euid := eus.Uid.Hex()
	this.mFireUse[euid] = euid
	return euid, true
}

// 放火匹配池 移除玩家
func (this *MatchPool) RemoveFireMatch(uid string) {
	this.rwFire.Lock()
	defer this.rwFire.Unlock()
	if _, ok := this.mFireProtect[uid]; ok {
		return
	}
	delete(this.mFireMatch, uid)
}

// 放火匹配池 添加玩家
func (this *MatchPool) AddFireUser(uid string) {
	this.rwFire.Lock()
	defer this.rwFire.Unlock()
	this.mFireMatch[uid] = uid
}

// 放火匹配池 移除已经使用
func (this *MatchPool) RemoveFireUse(uid string) {
	this.rwFire.Lock()
	defer this.rwFire.Unlock()
	delete(this.mFireUse, uid)
}

// 放火匹配池 是否已经使用
func (this *MatchPool) FireIsUse(uid string) (r string, ok bool) {
	this.rwFire.Lock()
	defer this.rwFire.Unlock()
	r, ok = this.mFireUse[uid]
	return
}

// 放火匹配池 添加保护玩家
func (this *MatchPool) AddFireProtec(uid string, endTime int64) {
	this.mFireProtect[uid] = true
	this.addFireProtec <- &ProtectionUser{Uid: uid, EndTime: endTime}
}

// 放火匹配池 移除受保护的玩家
func (this *MatchPool) RemoveFireProtec(uid string) {
	if _, ok := this.mFireProtect[uid]; !ok {
		return
	}
	this.removeFireProtec <- uid
}

// 放火匹配池 移除受保护的玩家
func (this *MatchPool) protectionsFireRemove(uid string) {
	// delete(this.mFireProtect, uid)
	// pros := make([]*ProtectionUser, 0)
	// for _, item := range this.protecFire {
	// 	if item.Uid != uid {
	// 		pros = append(pros, item)
	// 	}
	// }
	// this.protecFire = pros
}

func (this *MatchPool) Init() {
	this.rwFire = new(sync.RWMutex)
	this.mFireMatch = make(map[string]string)
	this.mFireUse = make(map[string]string)
	this.protecFire = make(PriorityQueue, 0)
	this.addFireProtec = make(chan *ProtectionUser, 1000)
	this.removeFireProtec = make(chan string, 1000)
	this.mFireProtect = make(map[string]bool)

	luser := make([]*m.User, 0)
	// err := dbmodule.MgoDB.GameDB.FindAll("user", bson.M{"gold": bson.M{"$gte": data.SlotLimitGold}}, bson.M{"_id": 1}, &objIds)
	err := dbmodule.MgoDB.GameDB.FindAll("user", nil, nil, &luser)
	if err != nil {
		log.WithFields(log.Fields{
			"err": err,
		}).Error("匹配池查找数据库失败")
		return
	}
	for _, item := range luser {
		uid := item.Uid.Hex()
		if item.MaxPos.LockBlockTid > 1 {
			this.mFireMatch[uid] = uid //玩家进入放火匹配池
		}
	}
}

func (this *MatchPool) Run(closeSig chan bool) {
	now := utils.TNow().Local()
	for {
		var effective time.Time
		if this.protecFire.Len() == 0 {
			effective = now.AddDate(10, 0, 0)
		} else {
			effective = time.Unix(this.protecFire[0].EndTime, 0)
		}

		select {
		case <-closeSig:
			return
		case now = <-time.After(effective.Sub(now)):
			for this.protecFire.Len() > 0 {
				EndTime := this.protecFire[0].EndTime
				if EndTime > effective.Unix() {
					break
				}
				item := heap.Pop(&this.protecFire).(*ProtectionUser)
				delete(this.mFireProtect, item.Uid)
				fmt.Println(item.EndTime)
			}
			continue
		case addPro := <-this.addFireProtec:
			heap.Push(&this.protecFire, addPro)
			// case delProUid := <-this.removeStealProtec:
			// 	this.protectionsStealRemove(delProUid)
		}
		now = utils.TNow().Local()
	}
}

func (this *MatchPool) Destroy() {

}
